#include "task.h"
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/epoll.h>
#include <sys/resource.h>
#include "log.h"

/**
 * param: size epoll_wait()'s event buffer size
 */
struct context *contextCreate(int size) {
    struct context *ctx;

    if (size <= 0) {
        struct rlimit rl;
        if (getrlimit(RLIMIT_NOFILE, &rl) < 0) {
            LOG(LOG_WARNING, "can't get file limit: getrlimit error: %s",
                    strerror(errno));
            size = 1024;
        } else
            size = rl.rlim_cur;
    }

    ctx = malloc(sizeof(*ctx) + sizeof(*ctx->events) * size);
    if (ctx == NULL) {
        errno = ENOMEM;
        return NULL;
    }

    if ((ctx->epollFd = epoll_create(size)) < 0) {
        int se = errno;
        free(ctx);
        errno = se;
        return NULL;
    }

    ctx->events = (struct epoll_event *) (ctx + 1);
    memset(ctx->events, 0, sizeof(*ctx->events) * size);
    ctx->size = size;

    return ctx;
}

void contextRelease(struct context *ctx) {
    if (ctx) {
        if (ctx->epollFd >= 0)
            close(ctx->epollFd);
        free(ctx);
    }
}

int contextAddTask(const struct context *ctx, struct task *task) {
    task->watchEvents.data.ptr = task;
    return epoll_ctl(ctx->epollFd, EPOLL_CTL_ADD, task->fd, &task->watchEvents);
}

int contextDeleteTask(const struct context *ctx, struct task *task) {
    struct epoll_event ev = { 0, { 0 } };
    return epoll_ctl(ctx->epollFd, EPOLL_CTL_DEL, task->fd, &ev);
}

int contextTaskSetEvents(const struct context *ctx, struct task *task, uint32_t events) {
    task->watchEvents.events |= events;
    return epoll_ctl(ctx->epollFd, EPOLL_CTL_MOD, task->fd, &task->watchEvents);
}

int contextTaskClearEvents(const struct context *ctx, struct task *task, uint32_t events) {
    task->watchEvents.events &= ~events;
    return epoll_ctl(ctx->epollFd, EPOLL_CTL_MOD, task->fd, &task->watchEvents);
}

int contextTaskResetEvents(const struct context *ctx, struct task *task, uint32_t events) {
    task->watchEvents.events = events;
    return epoll_ctl(ctx->epollFd, EPOLL_CTL_MOD, task->fd, &task->watchEvents);
}

int contextTaskChange(const struct context *ctx, struct task *task) {
    task->watchEvents.data.ptr = task;
    return epoll_ctl(ctx->epollFd, EPOLL_CTL_MOD, task->fd, &task->watchEvents);
}
